use std::{rc::Rc, cell::RefCell, fmt};

use super::Object;

// 存储数值和引用
#[derive(Clone, Debug)]
pub enum Slot {
    Num(i32), 
    Ref(*mut Object), // TODO Object指针现未进行回收处理
}

impl Slot {
    pub fn get_num(&self) -> Option<i32> {
        match self {
            Self::Num(val) => Some(*val),
            Self::Ref(_) => None
        }
    }

    pub fn get_ref(&self) -> Option<*mut Object> {
        match self {
            Self::Num(_) => None,
            Self::Ref(r) => Some(r.clone())
        }
    }
}

impl fmt::Display for Slot {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match self {
            Self::Num(v) => format!("Num: {}", v),
            Self::Ref(_) => format!("Ref")
            
        };
        write!(f, "{}", s)
    }
}

#[derive(Default, Debug, Clone)]
pub struct Slots(Vec<Option<Slot>>);

impl Slots {
    pub fn new_slots(max_locals: usize) -> Slots {
        Slots((0..max_locals).map(|_| None).collect())
    }
    
    pub fn set_int(&mut self, index:usize, val: i32) {
        self.0[index] = Some(Slot::Num(val));
    }

    pub fn get_int(&self, index: usize) -> i32 {
        // None，设置默认为0
        match self.0[index].as_ref() {
            Some(val) => val.get_num().unwrap_or(0),
            None => 0
        }
    }

    pub fn set_float(&mut self, index: usize, val: f32) {
        let bytes = val.to_be_bytes();
        self.0[index] = Some(Slot::Num(i32::from_be_bytes(bytes)));
    }

    pub fn get_float(&self, index: usize) -> f32 {
        let bytes = self.0[index].as_ref().unwrap().get_num().unwrap().to_be_bytes();
        f32::from_be_bytes(bytes)
    }

    pub fn set_long(&mut self, index: usize, val: i64) {
        self.0[index] = Some(Slot::Num((val >> 32) as i32));
        self.0[index + 1] = Some(Slot::Num(val as i32));
    }

    pub fn get_long(&self, index: usize) -> i64 {
        // 小转大，i32 -> i64
        // 如果源数据是无符号的，则进行零扩展(zero-extend)
        // 如果源数据是有符号的，则进行符号扩展(sign-extend)
        let high = self.get_int(index) as i64;
        let low = self.get_int(index + 1) as i64;
        (high << 32) | (low & 0xffffffff)
    }

    pub fn set_double(&mut self, index: usize, val: f64) {
        let bytes = val.to_be_bytes();
        self.set_long(index, i64::from_be_bytes(bytes));
    }

    pub fn get_double(&self, index: usize) -> f64 {
        let bytes = self.get_long(index).to_be_bytes();
        f64::from_be_bytes(bytes)
    }

    pub fn set_ref(&mut self, index: usize, _ref: Option<*mut Object>) {
        self.0[index] = if let Some(r) = _ref { Some(Slot::Ref(r)) } else { None };
    }

    pub fn get_ref(&self, index: usize) -> Option<*mut Object> {
        match self.0[index].as_ref() {
            Some(slot) => slot.get_ref(),
            None => None
        }
    }
}
